#include "Sound.h"
#include <string>

SoundObject::SoundObject()
	: m_pEngine(NULL)
	, m_pWaveBank(NULL)
	, m_pSoundBank(NULL)
	, m_pbWaveData(NULL)
	, m_pbSoundData(NULL)
{
	HRESULT hr = PrepareXACT();
	if (FAILED(hr))
	{
		::MessageBox(NULL, "Failed to init XACT.", "SoundObject", MB_OK);
		throw "Could not create sound engine!";
	}
}



SoundObject::~SoundObject()
{
	CleanupXACT();
}
bool FileExists(const char* cszFileName)
{
	return (cszFileName != NULL);
}
void SoundObject::CleanupXACT()
{
	/* Note that pEngine->ShutDown is synchronous and will take some time to complete 
	** if there are still playing cues.  Also pEngine->ShutDown() is generally only 
	** called when a game exits and is not the preferred method of changing audio 
	** resources. To know when it is safe to free wave/sound bank data without 
	** shutting down the XACT engine, use the XACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED 
	** or XACTNOTIFICATIONTYPE_WAVEBANKDESTROYED notifications 
	*/
	if (NULL != m_pEngine)
	{
		m_pEngine->ShutDown();
		m_pEngine->Release();
	}

	if (NULL != m_pbSoundData)
	{
		delete[] m_pbSoundData;
		m_pbSoundData = NULL;
	}

	if (NULL !=  m_pbWaveData)
	{
		delete[] m_pbWaveData;
		m_pbWaveData = NULL;
	}

	/* Turn off the COM objects mechanism */
	::CoUninitialize();
}
HRESULT SoundObject::PrepareXACT()
{
	/* Start up COM object subsystem */
	HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);  // COINIT_APARTMENTTHREADED will work too
	if (SUCCEEDED(hr))
	{
#ifdef _DEBUG
		DWORD dwCreationFlags = XACT_FLAG_API_DEBUG_MODE;
#else
		DWORD dwCreationFlags = 0;
#endif
		/* Create the sound engine */
		hr = ::XACT3CreateEngine(dwCreationFlags, &m_pEngine);
		if (SUCCEEDED(hr) && (NULL != m_pEngine))
		{
			/* Initialize & create the XACT runtime */
			XACT_RUNTIME_PARAMETERS xrParams = {0};
			xrParams.lookAheadTime = XACT_ENGINE_LOOKAHEAD_DEFAULT;
			return m_pEngine->Initialize(&xrParams);
		}
	}
	return hr;
}
HRESULT SoundObject::LoadSounds(const char* cszName)
{
	std::string sName(cszName);

	/***** Create an "in memory" XACT wave bank *****/

	HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);	// guilty until proven innocent


	std::string sSoundsFile = sName + ".xwb";						/* Read wave bank data into memory and hand off to XACT3 for use */
	DWORD dwWavesSize = LoadFileToMemory(sSoundsFile.c_str(), m_pbWaveData);
	if (0 < dwWavesSize)
	{
		HRESULT hr = m_pEngine->CreateInMemoryWaveBank(m_pbWaveData, dwWavesSize, 0, 0, &m_pWaveBank);
		if (FAILED(hr))
		{
			CleanupXACT();
			return hr;
		}
	}

	/* Read sound bank data into memory and hand off to XACT3 for use */
	sSoundsFile = sName + ".xsb";
	DWORD dwSoundsSize = LoadFileToMemory(sSoundsFile.c_str(), m_pbSoundData);
	if (0 < dwSoundsSize)
	{
		HRESULT hr = m_pEngine->CreateSoundBank(m_pbSoundData, dwSoundsSize, 0, 0, &m_pSoundBank);
		if (FAILED(hr))
		{
			CleanupXACT();
			return hr;
		}
	}
	return S_OK;

}

DWORD SoundObject::LoadFileToMemory(const char* cszFileName, void*& pDataPointer)
{
	DWORD dwFileSize = 0;
	pDataPointer = NULL;

	/* Assume that the file won't be found */
	HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	HANDLE hFile = ::CreateFile(cszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if (INVALID_HANDLE_VALUE != hFile)
	{
		/* File opened. How big is it? */
		dwFileSize = ::GetFileSize(hFile, NULL);
		if (0 < dwFileSize)
		{
			/* Allocate space for the data and free that space later, after
			** receiving "bank destroyed" notification
			*/
			pDataPointer = new BYTE[dwFileSize];
			if (NULL != pDataPointer)
			{
				/* How many bytes does ReadFile load? */
				DWORD dwBytesRead;
				if (0 != ::ReadFile(hFile, pDataPointer, dwFileSize, &dwBytesRead, NULL))
				{
					hr = S_OK;
				}
			}
		}
		::CloseHandle(hFile);
	}
	if (FAILED(hr))
	{
		return -1;
	}
	return dwFileSize;
}
WORD SoundObject::GetCue(const char* cszCue)
{
	/* Note that if the cue does not exist in the sound bank, the index will be
	** XACTINDEX_INVALID however this is ok especially during development.  The
	** Play or Prepare call will just fail.
	*/
	return (NULL == m_pSoundBank) ? 0 : (WORD)m_pSoundBank->GetCueIndex(cszCue);
}
HRESULT SoundObject::PlayCue(WORD wCueIndex)
{
	return (NULL == m_pSoundBank) ? E_FAIL : m_pSoundBank->Play(wCueIndex, 0, 0, NULL);
}


/*****************************************************\
* Play Cue: play sounds associated with cue name      *
\*****************************************************/
HRESULT SoundObject::PlayCue(const char* cszCue)
{
	return PlayCue(GetCue(cszCue));
}
